package org.example.ctsbocc.interceptor;

import jakarta.annotation.Resource;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.example.ctsbocc.entity.User;
import org.example.ctsbocc.service.UserService;
import org.example.ctsbocc.utils.JwtUtil;
import org.example.ctsbocc.utils.LoggerUtil;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import java.time.LocalDateTime;

/**
 * JWT令牌拦截器
 * 用于验证HTTP请求中的JWT令牌有效性，实现接口访问控制
 */
@Component("jwtInterceptor")
public class JwtInterceptor implements HandlerInterceptor {

    /**
     * 注入JWT工具类实例
     * 用于生成和验证JWT令牌
     */
    @Resource(name = "jwtUtil")
    private JwtUtil jwtUtil;

    @Resource(name = "userService")
    private UserService userService;

    /**
     * 请求预处理方法
     * 在目标处理器执行前调用，用于验证请求的合法性
     *
     * @param request  HTTP请求对象
     * @param response HTTP响应对象
     * @param handler  被调用的处理器对象
     * @return true-允许继续执行处理器，false-中断请求处理
     * @throws Exception 处理异常
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 允许OPTIONS预检请求通过
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            return true;
        }

        // 从请求头中提取JWT令牌
        String token = extractToken(request);
        // 从请求头中提取key
        String key = request.getHeader("key");
//        System.out.println(key);

        // 获取当前会话
        HttpSession session = request.getSession();
        // 获取当前Cookie
        Cookie[] cookies = request.getCookies();

        // 如果header中没有token，尝试从Cookie中获取userToken
        if (token == null && cookies != null) {
            token = extractCookieValue(cookies, "userToken");
        }

        // 如果header中没有key，尝试从Cookie中获取key
        if (key == null && cookies != null) {
            key = extractCookieValue(cookies, "key");
        }

        if (session.isNew() && token != null && key != null) {
            try {
                LocalDateTime time = LocalDateTime.parse(key);
                String userId = jwtUtil.validateToken(token, time);
                User user = userService.selectById(Long.valueOf(userId));

                if (user != null) {
                    session.setAttribute("user", user);
                    session.setAttribute("key", key);
                    LoggerUtil.log().info("通过header恢复会话成功: 用户{}", user.getGitUsername());
                    return true;
                }
            } catch (Exception e) {
                LoggerUtil.log().warn("会话恢复失败", e);
            }
        }

        // 验证令牌有效性
        if (token != null && isTokenValid(token, key, (User) session.getAttribute("user"))) {
            LoggerUtil.log().info("验证通过，允许访问");
            return true; // token有效，允许访问
        }

        // 令牌无效时返回401未授权状态码
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        LoggerUtil.log().warn("无效的token: {} \n请求: {}", token, request.getRequestURI());
        return false;
    }


    /**
     * 验证JWT令牌有效性
     *
     * @param token   JWT令牌字符串
     * @return true-令牌有效，false-令牌无效
     */
    private boolean isTokenValid(String token, String key, User user) {
        try {
            // 调用JWT工具类验证令牌
            return jwtUtil.validateToken(token, key, user);
        } catch (Exception e) {
            LoggerUtil.log().warn("令牌验证异常: {}", e.getMessage());
            // 捕获验证异常，返回false表示令牌无效
            return false;
        }
    }

    /**
     * 从HTTP请求头中提取Bearer Token
     *
     * @param request HTTP请求对象
     * @return 提取到的JWT令牌，未找到则返回null
     */
    private static String extractToken(HttpServletRequest request) {
        // 获取Authorization请求头
        String bearerToken = request.getHeader("Authorization");
        // 检查是否为Bearer Token格式
        if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
            // 提取Bearer后的令牌部分
            return bearerToken.substring(7);
        }
        return null;
    }

    private static String extractCookieValue(Cookie[] cookies, String cookieName) {
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if (cookie.getName().equals(cookieName)) {
                    System.out.println(cookie.getName() + " " + cookie.getValue());
                    return cookie.getValue();
                }
            }
        }
        return null;
    }
}
